const fragmentShaderSource = `
precision highp float;
  uniform sampler2D u_particlesTexture;
  uniform float u_rand_seed;
  in vec2 v_textureCoordinates;

  // pseudo-random generator
  const vec3 rand_constants = vec3(12.9898, 78.233, 4375.85453);
  float rand(const vec2 co) {
      float t = dot(rand_constants.xy, co);
      return fract(sin(t) * (rand_constants.z + t));
  }

  vec3 lookup_wind(const vec3 particle_pos) {
    bool shouldFlipY = Weget_shouldFlipY() > 0.0;
    vec3 uv = particle_pos;
    if(!shouldFlipY){
      uv.y = 1.0 - uv.y;
    }
    return WeDecodeComponent(uv).xyz;
  }

  bool outOfExtent(vec3 pos) {
    vec4 worldExtent = Weget_extent();
    bool repeatX = (worldExtent.z - worldExtent.x) == czm_twoPi;

    return (pos.y > 1.0 || pos.y < 0.0) ||
    (!repeatX && (pos.x > 1.0 || pos.x < 0.0));
  }

  void main() {
    float zSpeedFactor = Weget_zSpeedFactor();

    float limitMinZSpeed =  Weget_limitMinZSpeed();
    float limitMaxSpeed = Weget_limitMaxSpeed();
    float maxValue = Weget_maxValue();
    float u_drop_rate = Weget_drop_rate();
    float drop_rate_bump = Weget_drop_rate_bump();
    float speed_factor = Weget_speed_factor();
    speed_factor *= Weget_dynamicSpeedFactor();
    float limitMinSpeed = Weget_limitMinSpeed();
    float clipMinX = Weget_clipMinX();
    float clipMaxX = Weget_clipMaxX();
    float clipMinY = Weget_clipMinY();
    float clipMaxY = Weget_clipMaxY();
    float clipMinZ = Weget_clipMinZ();
    float clipMaxZ = Weget_clipMaxZ();
    float filterMin = Weget_filterMin();
    vec4 viewExtent = Weget_viewExtent();
    vec4 worldExtent = Weget_extent();
    vec2 ws = worldExtent.xz;
    vec2 en = worldExtent.yw;

    vec2 uv = v_textureCoordinates;

    vec4 pos = texture(u_particlesTexture, uv);
    vec3 velocity = lookup_wind(pos.xyz);
    float speed = length(velocity);

    if(speed > limitMaxSpeed){
      velocity *= limitMaxSpeed / speed;
    }

    float speedRate = speed / maxValue;

    if(speedRate > 1.0) speedRate = 1.0;

    float d1 = 180.0;
    float d2 = 90.0;
    vec2 lonlat = WeGetImageryLonLat(vec2(pos.x,1.0 - pos.y),worldExtent);

    bool inViewExtent = lonlat.x < viewExtent.x
      || lonlat.x > viewExtent.z
      || lonlat.y < viewExtent.y
      || lonlat.y > viewExtent.w;

    float viewWidth = (viewExtent.z - viewExtent.x) / (worldExtent.z - worldExtent.x);
    float viewHeight = (viewExtent.w - viewExtent.y) / (worldExtent.w - worldExtent.y);
    float r1 = min(viewWidth,viewHeight);

    float dL = (lonlat.y + czm_piOverTwo) / czm_pi;
    float distortion = cos(radians(dL * d1 - d2));
    if(distortion == 0.0) distortion = 1.0;
    float dr = 1.0 / max((ws.y - ws.x)/czm_twoPi,(en.y - en.x)/czm_pi);
    vec3 offset = vec3(velocity.x / distortion, -velocity.y, velocity.z) * 0.00001 * speed_factor;

    offset.xyz *= dr;
    offset.z *= zSpeedFactor;
    pos.xyz += offset;

    vec2 seed = uv * fract(u_rand_seed + 0.5);
    float drop_rate = u_drop_rate + speedRate * drop_rate_bump;

    if(abs(offset.z) < limitMinZSpeed){
      drop_rate += 0.1;
    }

    float drop = step(1.0 - drop_rate, rand(seed));

    float frameNumber1 = pos.w;

    if(outOfExtent(pos.xyz)
    || pos.x < clipMinX
    || pos.x > clipMaxX
    || pos.y < clipMinY
    || pos.y > clipMaxY
    || pos.z < clipMinZ
    || pos.z > clipMaxZ
    || speed == 0.0
    || length(velocity) < filterMin
    || length(velocity) < limitMinSpeed
    || drop == 1.0
    || inViewExtent){
      vec3 random_pos = fract(vec3(
          rand(seed + 1.3),
          rand(seed + 2.1),
          rand(seed + 0.05)));

      // if(pos.xy != uv.xy){
      //   random_pos.xy = uv.xy;
      // }

      random_pos.x *= viewWidth;
      random_pos.y *= viewHeight;
      random_pos.x += (viewExtent.x - worldExtent.x) / (worldExtent.z - worldExtent.x);
      random_pos.y += 1.0 - (viewExtent.w - worldExtent.y) / (worldExtent.w - worldExtent.y);
      pos = vec4(random_pos,0.0);
      frameNumber1 = czm_frameNumber;
    }

    pos.xyz = fract(pos.xyz);

    out_FragColor = vec4(pos.xyz,frameNumber1);
  }
`

export default { fragmentShaderSource }
